package com.example.java_gobang.model;

import com.example.java_gobang.JavaGobangApplication;
import com.example.java_gobang.game.OnlineUserManager;
import com.example.java_gobang.game.RoomManager;
import com.example.java_gobang.mapper.UserMapper;
import com.example.java_gobang.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

//这个类表示一个游戏房间
public class Room {
    //使用字符串类型来表示,方便生成唯一值
    private String roomId;

    private User user1;
    private User user2;

    //先手方
    private int whiteUser;

    private static final int MAX_ROW=15;
    private static final int MAX_COL=15;

    //这个二维数组用来表示棋牌
    //约定:
    // 1.使用0表示当前位置未落子,初始化好的int二维数组就相当于是全0
    //2.使用1表示user1的落子位置
    //2.使用2表示user2的落子位置
    private int[][]board=new int[MAX_ROW][MAX_COL];

    //创建ObjectMapper用来转换json
    private ObjectMapper objectMapper=new ObjectMapper();

    //引入onlineUserManager
    //@Autowired 不能通过自动注入进行初始化
    private OnlineUserManager onlineUserManager;

    private UserMapper userMapper;

    //用于房间销毁
    //@Autowired
    private RoomManager roomManager;

    //通过这个方法来处理一次落子操作
    //要做的事情:
    public void putChess(String reqJson) throws IOException {
        //1.记录当前落子的位置.
        GameRequest request=objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response=new GameResponse();
        //当前这个子是玩家1落的还是玩家2落的.根据这个玩家1和玩家2来决定往数组写1(玩家1)还是2(玩家2)
        int chess=request.getUserId()==user1.getUserId()?1:2;
        int row=request.getRow();
        int col=request.getCol();
        if(board[row][col]!=0){
            //在客户端已经针对重复落子进行判定了,此处为了程序更加稳健,在服务器也判断下是否重复落子了
            System.out.println("当前位置("+row+","+col+")已经落子了");
            return;
        }
        board[row][col]=chess;

        //2.打印当前的棋牌信息,以方便观察局势,也方便后面验证胜负关系
        printBoard();

        //3.进行胜负判定
        int winner=checkWinner(row,col,chess);

        //4.给房间中的所有客户端返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());

        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        //要想给用户发送websocket发送数据,就需要获取到这个用户的websocketSession
       WebSocketSession session1= onlineUserManager.getFromGameRoom(user1.getUserId());
       WebSocketSession session2=onlineUserManager.getFromGameRoom(user2.getUserId());
       //万一当前查到的会话为空(玩家已经下线了)特殊处理
        if(session1==null){
            //如果玩家1已经下线了,那么就认为玩家2直接获胜
            response.setWinner(user2.getUserId());
            System.out.println("玩家1掉线了!");
        }
        if(session2==null){
            //如果玩家1已经下线了,那么就认为玩家2直接获胜
            response.setWinner(user1.getUserId());
            System.out.println("玩家2掉线了!");
        }
        //把响应构成JSON字符串,通过session进行传输
        String respJson=objectMapper.writeValueAsString(response);
        if(session1!=null){
            session1.sendMessage(new TextMessage(respJson));
        }
        if(session2!=null){
            session2.sendMessage(new TextMessage(respJson));
        }

        //5..如果当前胜负已分,此时这个房间就失去存在的意义,就可以销毁房间,
        //可以把房间从房间管理器 的移除
        if(response.getWinner()!=0){
            //胜负已分
            System.out.println("游戏结束!房间即将销毁 !roomId="+roomId+"获胜方 "+response.getWinner());
            //更新获胜方和失败方的信息
            int winUserId=response.getWinner();
            int loseUserId=response.getWinner()==user1.getUserId()?user2.getUserId():user1.getUserId();

            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);

            //销毁房间
            roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
        }
    }

    private void printBoard() {
        //打印棋牌信息
        System.out.println("[打印棋牌信息] "+roomId);
        System.out.println("=====================================");
        for(int r=0;r<MAX_ROW;r++){
            for(int c=0;c<MAX_COL;c++){
                System.out.print(board[r][c]+" ");
            }
            //每遍历一行之后再换行
            System.out.println();
        }
        System.out.println("=====================================");
    }

    //使用这个方法来判断是否分出胜负
    //如果玩家1获胜返回玩家1的userId,如果玩家2获胜返回玩家2的userId
    //如果未分出胜负就返回0
    private int checkWinner(int row, int col,int chess) {
        //1.检测所有的行
        //先遍历这五种情况
        for (int c=col-4;c<=col;c++){
            //针对其中的一个情况,来判定这个五个是不是连在一起
            //不光是这个五个子连着,而且还得和玩家落的子是一样的(才算获胜)
            try {
                if(board[row][c]==chess &&board[row][c+1]==chess
                        &&board[row][c+2]==chess&&board[row][c+3]==chess
                        &&board[row][c+4]==chess){
                    //构成了五子连珠!胜负已分!
                    return  chess==1?user1.getUserId():user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里忽略这个异常
                continue;
            }
        }
        //2.检测所有列
        for(int  r=row-4;r<=row;r++){
            try {
                if(board[r][col]==chess&&board[r+1][col]==chess
                &&board[r+2][col]==chess&&board[r+3][col]==chess
                &&board[r+4][col]==chess){
                    return  chess==1?user1.getUserId():user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里忽略这个异常
                continue;
            }
        }
        //3.检测左对角线
        for(int r=row-4,c=col-4;r<=row&&c<=col;r++,c++){
            try{
                if(board[r][c]==chess&&board[r+1][c+1]==chess
                &&board[r+2][c+2]==chess&&board[r+3][c+3]==chess
                &&board[r+4][c+4]==chess)
                    return  chess==1?user1.getUserId():user2.getUserId();
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里忽略这个异常
                continue;
            }
        }
        //4.检测右对角线
        for(int r=row-4,c=col+4;r<=row&&c>=col;r++,c--){
            try {
                if(board[r][c]==chess&&board[r+1][c-1]==chess
                &&board[r+2][c-2]==chess&&board[r+3][c-3]==chess
                &&board[r+4][c-4]==chess){
                    return  chess==1?user1.getUserId():user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里忽略这个异常
                continue;
            }
        }
        //胜负未来分
        return 0;
    }

    public int getWhiteUser() {
        return whiteUser;
    }

    public void setWhiteUser(int whiteUser) {
        this.whiteUser = whiteUser;
    }


    public Room(){
        //构造Room的时候生成唯一的字符串表示房间Id;
        //使用UUID来作为房间id(表示唯一的房间)
        roomId= UUID.randomUUID().toString();
        //通过入口类记录的context来手动获取到前面的RoomManager和onlineUserManager
        onlineUserManager= JavaGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager=JavaGobangApplication.context.getBean(RoomManager.class);
        userMapper=JavaGobangApplication.context.getBean(UserMapper.class);
    }

    public String getRoomId() {
        return roomId;
    }

    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }

    public User getUser1() {
        return user1;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser2() {
        return user2;
    }

    public void setUser2(User user2) {
        this.user2 = user2;
    }
}
